package com.lee.mq.mqserver.core;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lishuo
 * Date: 2023-10-26
 * Time: 12:06
 */

import com.lee.mq.common.MqException;

/**
 * 来实现交换机的转发规则.同时验证bindingKey和routingKey是否合法
 */
public class Router {
    /**
     * 检验routingKey是否合格
     * @param routingKey
     * @return
     */
    public boolean checkRoutingKey(String routingKey) {
        // 长度为0为合法。如：扇出交换机
        if (routingKey.length() == 0) {
            return true;
        }

        for (int i = 0; i < routingKey.length(); i++) {
            char ch = routingKey.charAt(i);
            // 1.大写字母
            if (ch >= 'A' && ch <= 'Z') {
                continue;
            }
            // 2.小写字母
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            // 3.数字
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            // 4.点 . 和下划线 _
            if (ch == '.' || ch == '_') {
                continue;
            }
            // 如果不满足以上规则，则视为无效
            return false;
        }
        return true;
    }

    /**
     * 检验bindingKey是否合格
     * @param bindingKey
     * @return
     */
    public boolean checkBindingKey(String bindingKey) {
        // 长度为0为合法。如：扇出交换机、直接交换机（显然这里不可能）
        if (bindingKey.length() == 0) {
            return true;
        }
        for (int i = 0; i < bindingKey.length(); i ++) {
            char ch = bindingKey.charAt(i);
            // 1.大写字母
            if (ch >= 'A' && ch <= 'Z') {
                continue;
            }
            // 2.小写字母
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            // 3.数字
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            // 4.点 . 下划线 _ 星号 * 井号 #
            if (ch == '.' || ch == '_' || ch == '*' || ch == '#') {
                continue;
            }
            return false;
        }
        // 检查 * 和 # 是否为独立的部分（位于..之间，有且只有一个）
        String[] bindingTokens = bindingKey.split("\\.");
        for (String token : bindingTokens) {
            if (token.length() > 1 && (token.contains("*") || token.contains("#"))) {
                return false;
            }
        }
        // 特殊规定（特殊相邻情况）
        // 1. aaa.#.#.bbb    => 非法
        // 2. aaa.#.*.bbb    => 非法
        // 3. aaa.*.#.bbb    => 非法
        // 4. aaa.*.*.bbb    => 合法
        for (int i = 0; i < bindingTokens.length - 1; i++) {
            if (bindingTokens[i].equals("#") && bindingTokens[i+1].equals("#")) {
                return false;
            }
            if (bindingTokens[i].equals("#") && bindingTokens[i+1].equals("*")) {
                return false;
            }
            if (bindingTokens[i].equals("*") && bindingTokens[i+1].equals("#")) {
                return false;
            }
        }
        return true;
    }

    /**
     * FANOUT、TOPIC 规则校验
     * @param exchangeType
     * @param binding
     * @param message
     * @return
     * @throws MqException
     */
    public boolean route(ExchangeType exchangeType,Binding binding,Message message) throws MqException {
        if (exchangeType == ExchangeType.FANOUT) {
            // 扇出交换机挨个转发，无需判断
            return true;
        } else if (exchangeType == ExchangeType.TOPIC) {
            // 主题交换机需要根据相关规则校验
            return routeTopic(binding,message);
        } else {
            throw new MqException("[Roter] 交换机不存在: exchangeType = "+exchangeType);
        }
    }

    /**
     * TOPIC 具体校验规则
     * @param binding
     * @param message
     * @return
     */
    private boolean routeTopic(Binding binding, Message message) {
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");
        // 这里使用到一个小算法(双指针)
        // 1.初始化两个下标，分别指向bindingTokens、routingTokens开始位置
        int bindingIndex = 0;
        int routingIndex = 0;
        while (bindingIndex < bindingTokens.length && routingIndex < routingTokens.length) {
            // 2.开始向后遍历，bindingKey中遇到 * 两个指针分别后移一步
            if (bindingTokens[bindingIndex].equals("*")) {
                bindingIndex ++;
                routingIndex ++;
                continue;
            } else if (bindingTokens[bindingIndex].equals("#")) {
                // 3.bindingKey中遇到#，检查bindingKey后是否还有字符，如果没有直接返回true
                if (bindingIndex + 1 == bindingTokens.length) {
                    return true;
                }
                // 4.如果还存在字符，则找到routingTokens中对应的bindingTokens中#后的下一个字符串
                int res = findTokenIndex(routingTokens,routingIndex,bindingTokens[bindingIndex+1]);
                // 找到下标，找不到为 -1
                if (res == -1) {
                    return false;
                } else {
                    bindingIndex = bindingIndex + 2;
                    routingIndex = res +1;
                }
            } else {
                // 5.继续比对,如果是普通字符串，则比对是否相等
                if (!bindingTokens[bindingIndex].equals(routingTokens[routingIndex])) {
                    return false;
                }
                routingIndex ++;
                bindingIndex ++;
            }
        }
        // 6.成功条件：二者同时达到末尾
        if (bindingIndex == bindingTokens.length && routingIndex == routingTokens.length) {
            return true;
        }
        return false;
    }

    // 暂时规定不存在这种重复情况：
    // aaa.bb.bb.cc
    // aaa.#.bb.cc
    // aaa.bb.cc.bb.cc
    // 从前往后找
    private int findTokenIndex(String[] routingTokens, int routingIndex, String bindingToken) {
        for (int i = routingIndex; i < routingTokens.length; i++) {
            if (routingTokens[i].equals(bindingToken)) {
                return i;
            }
        }
        return -1;
    }
}
